package org.csu.pm.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * 用于生成 token、校验 token
 *
 * @author Kwanho
 */
@ConfigurationProperties(prefix = "jwt")
@Component
@Slf4j
public class JwtUtil {
    @Setter
    private String secret; // 加密算法所需要的密钥
    @Setter
    private long defaultExpireTime; // 默认过期时间
    @Setter
    private String expireUnit; // 过期时间的单位
    public static final long DEFAULT = -1;
    public static final long SECOND = 1000;
    public static final long MINUTE = 1000 * 60;
    public static final long HOUR = 1000 * 60 * 60;
    public static final long DAY = 1000 * 60 * 24;
    public static final long WEEK = 1000 * 60 * 24 * 7;
    public static final long MONTH = 1000 * 60 * 24 * 30;

    /**
     * 生成包含用户id的token
     *
     * @param userId     用户id
     * @param expireTime 过期时间
     * @return token
     * @author Kwanho
     */
    public String createJwtToken(String userId, long expireTime) {
        if (expireTime == DEFAULT)
            expireTime = defaultExpireTime * getTimeUnit();
        Date date = new Date(System.currentTimeMillis() + expireTime);
        Algorithm algorithm = Algorithm.HMAC256(secret);

        return JWT.create()
                .withClaim("userId", userId)
                .withExpiresAt(date) // 设置过期时间
                .sign(algorithm);     // 设置签名算法
    }

    /**
     * 生成包含自定义信息的token
     *
     * @param map        自定义信息-键值对
     * @param expireTime 过期时间
     * @return token
     * @author Kwanho
     */
    public String createJwtToken(Map<String, String> map, long expireTime) {
        if (expireTime == DEFAULT)
            expireTime = defaultExpireTime * getTimeUnit();
        JWTCreator.Builder builder = JWT.create();
        if (MapUtils.isNotEmpty(map)) {
            map.forEach(builder::withClaim);
        }
        Date date = new Date(System.currentTimeMillis() + expireTime);
        Algorithm algorithm = Algorithm.HMAC256(secret);
        return builder
                .withExpiresAt(date) // 过期时间
                .sign(algorithm);    // 签名算法
    }

    /**
     * 校验token是否合法
     *
     * @param token token
     * @return 是否正确
     * @author Kwanho
     */
    public boolean verifyToken(String token) {
        try {
            JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * 获取token中的userId
     *
     * @param token token
     * @return token中包含的用户id
     * @author Kwanho
     */
    public String getUserId(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("userId").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 获取token的负载
     *
     * @param token token
     * @param key   负载的键
     * @return 对应的值
     * @author Kwanho
     */
    public String getTokenAttr(String token, String key) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(key).asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 获取token中的所有负载
     *
     * @param token token
     * @return Map形式的负载
     * @author Kwanho
     */
    public Map<String, String> getAllTokenPayloads(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            Map<String, String> payload = new HashMap<>();
            jwt.getClaims().forEach((k, v) -> payload.put(k, v.asString()));
            return payload;
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 判断token是否过期
     *
     * @param token token
     * @return 是否过期
     * @author Kwanho
     */
    public boolean isExpire(String token) {
        DecodedJWT jwt;
        try {
            jwt = JWT.decode(token);
        } catch (Exception e) {
            return true;
        }
        // 如果token的过期时间小于当前时间，则表示已过期，为true
        return jwt.getExpiresAt().getTime() < System.currentTimeMillis();
    }

    /**
     * 从配置文件获得时长单位
     *
     * @return 单位
     * @author Kwanho
     */
    private long getTimeUnit() {
        long timeUnit;
        switch (this.expireUnit) {
            case "MINUTE":
                timeUnit = MINUTE;
                break;
            case "HOUR":
                timeUnit = HOUR;
                break;
            case "DAY":
                timeUnit = DAY;
                break;
            case "WEEK":
                timeUnit = WEEK;
                break;
            case "MONTH":
                timeUnit = MONTH;
                break;
            case "SECOND":
            default:
                timeUnit = SECOND;
                break;
        }
        return timeUnit;
    }
}
